#include "JSONData.h"
#include <sstream>
#include <vector>

JSON_Object::JSON_Object(std::string str)
{
	_type = JSON_OBJECT;

	size_t s = str.find_first_of("{") + 1;
	size_t e = str.find_last_of("}");
	_valueStr = str.substr(s, e-s);
	//std::cout << str << std::endl;
	//std::cout << _valueStr << std::endl;

	startIt = _valueStr.begin();
	endIt = _valueStr.end();
}

JSON_Abstract *	JSON_Object::getValueForKey(std::string name)
{
	for (; startIt != endIt; ++startIt)
	{
		std::string key = json::getKey(startIt, endIt);
		if (key.compare(name) == 0)
		{
			JSON_Abstract * data = json::getValue(startIt, endIt);
			json::cleanUp(startIt, endIt);
			return data;
		}
		else
			json::skipValue(startIt, endIt);
		json::cleanUp(startIt, endIt);

		if (startIt == endIt) break;
	}

	return NULL;
}

JSON_Array::JSON_Array(std::string str)
{
	_type = JSON_ARRAY;

	size_t s = str.find_first_of("[") + 1;
	size_t e = str.find_last_of("]");
	_valueStr = str.substr(s, e-s);

	startIt = _valueStr.begin();
	endIt = _valueStr.end();
}

JSON_Abstract *	JSON_Array::getValueById(unsigned int elementId)
{
	return NULL;
}

unsigned int JSON_Array::getElementCount()
{
	std::string::iterator itStart = startIt;
	json::removeSpaces(itStart, endIt);

	switch(*itStart)
	{
	case '{':
		return json::countObjectOrArrayElements(itStart, endIt);
		break;
	case '[':
		return json::countObjectOrArrayElements(itStart, endIt);
		break;
	case '"':
		return json::countStringElements(itStart, endIt);
		break;
	default: // numbers should be added
		break;
	}

	return 0;
}

JSON_String::JSON_String(std::string str)
{
	_type = JSON_STRING;
	_valueStr = str;
}

std::string	JSON_String::str()
{
	return "";
}

JSON_Number::JSON_Number(std::string str)
{
	_type = JSON_NUMBER;
	_valueStr = str;
}

float JSON_Number::number()
{
	return 0;
}

JSON_Boolean::JSON_Boolean(std::string str)
{
	_type = JSON_BOOLEAN;
	_valueStr = str;
}

bool JSON_Boolean::boolean()
{
	return false;
}

JSON_Null::JSON_Null(std::string str)
{
	_type = JSON_NULL;
	_valueStr = str;
}

std::string	json::getKey(std::string::iterator& startIt, std::string::iterator& endIt)
{
	removeSpaces(startIt, endIt);
	startIt++;

	std::string key;
	for (; startIt != endIt; ++startIt)
	{
		if (*startIt != '"')
			key += *startIt;
		else
		{
			startIt++;
			break;
		}
	}
	return key;
}

JSON_Abstract* json::getValue(std::string::iterator& startIt, std::string::iterator& endIt)
{
	removeSpaces(startIt, endIt);

	if (*startIt != ':')
	{
		std::cout << "Error : getValue should be :\n";
		return NULL;
	}
	startIt++; // skip :
	removeSpaces(startIt, endIt);

	switch(*startIt)
	{
	case '{':
		return json::getObjectValue(startIt, endIt);
		break;
	case '[':
		return json::getArrayValue(startIt, endIt);
		break;
	case '"':
		return json::getStringValue(startIt, endIt);
		break;
	default:
		break;
	}

	return NULL;
}

void json::cleanUp(std::string::iterator& startIt, std::string::iterator& endIt)
{
	removeSpaces(startIt, endIt);
	if (startIt != endIt && *startIt == ',')
	{
		startIt++;
		return;
	}
}
JSON_Abstract* json::getObjectValue(std::string::iterator& startIt, std::string::iterator& endIt)
{
	std::vector<JSON_Type> stack;
	removeSpaces(startIt, endIt);

	if (*startIt != '{')
	{
		std::cout << "getObjectValue Error : no {\n";
		return NULL;
	}

	std::stringstream ss;
	bool valueFound = false;
	//stack.push_back(JSON_OBJECT);
	//startIt++; // skip { // no need to skip
	//removeSpaces(startIt, endIt);

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '{') stack.push_back(JSON_OBJECT);
		else if (*startIt == '[') stack.push_back(JSON_ARRAY);
		else if (*startIt == '}') stack.pop_back(); // need to check top
		else if (*startIt == ']') stack.pop_back(); // need to check top
		ss << *startIt;

		if (stack.empty())
		{
			valueFound = true;
			startIt++; // skip }
			break;
		}
	}
	if (valueFound == true)
	{
		return new JSON_Object(ss.str());
	}
	return NULL;
}

JSON_Abstract* json::getArrayValue(std::string::iterator& startIt, std::string::iterator& endIt)
{
	std::vector<JSON_Type> stack;
	removeSpaces(startIt, endIt);

	if (*startIt != '[')
	{
		std::cout << "getObjectValue Error : no [\n";
		return NULL;
	}

	std::stringstream ss;
	bool valueFound = false;
	//stack.push_back(JSON_OBJECT);
	//startIt++; // skip { // no need to skip
	//removeSpaces(startIt, endIt);

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '{') stack.push_back(JSON_OBJECT);
		else if (*startIt == '[') stack.push_back(JSON_ARRAY);
		else if (*startIt == '}') stack.pop_back(); // need to check top
		else if (*startIt == ']') stack.pop_back(); // need to check top
		ss << *startIt;

		if (stack.empty())
		{
			valueFound = true;
			startIt++; // skip ]
			break;
		}
	}
	if (valueFound == true)
	{
		return new JSON_Array(ss.str());
	}
	return NULL;
}
JSON_Abstract* json::getStringValue(std::string::iterator& startIt, std::string::iterator& endIt)
{
	std::stringstream ss;
	startIt++; // skip "
	bool valueFound = false;

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '"')
		{
			valueFound = true;
			break;
		}
		ss << *startIt;
	}
	if (valueFound == true)
		startIt++;

	return new JSON_String(ss.str());
}

void json::skipValue(std::string::iterator& startIt, std::string::iterator& endIt)
{
	removeSpaces(startIt, endIt);
	if (*startIt != ':')
	{
		std::cout << "skipValue error : no colon\n";
		return;
	}
	startIt++; // skip :
	removeSpaces(startIt, endIt);

	switch(*startIt)
	{
	case '{':
		return json::skipObjectOrArray(startIt, endIt);
		break;
	case '[':
		return json::skipObjectOrArray(startIt, endIt);
		break;
	case '"':
		return json::skipString(startIt, endIt);
		break;
	default:
		break;
	}
}

void json::skipObjectOrArray(std::string::iterator& startIt, std::string::iterator& endIt)
{
	std::vector<JSON_Type> stack;
	removeSpaces(startIt, endIt);

	switch (*startIt)
	{
	case '{':
		stack.push_back(JSON_OBJECT);
		break;
	case '[':
		stack.push_back(JSON_ARRAY);
		break;
	}
	startIt++; // skip { or [
	removeSpaces(startIt, endIt);

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '{') stack.push_back(JSON_OBJECT);
		else if (*startIt == '[') stack.push_back(JSON_ARRAY);
		else if (*startIt == '}') stack.pop_back(); // need to check top
		else if (*startIt == ']') stack.pop_back(); // need to check top

		if (stack.empty())
		{
			startIt++;
			break;
		}
	}
}

void json::skipString(std::string::iterator& startIt, std::string::iterator& endIt)
{
	//removeSpaces(startIt, endIt);
	//if (*startIt != ':')
	//{
	//	std::cout << "skipString error : no colon\n";
	//	return;
	//}
	//startIt++; // skip :
	removeSpaces(startIt, endIt);

	if (*startIt != '"')
	{
		std::cout << "skipString error : no quotation\n";
		return;
	}

	startIt++; //skip "
	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '"')
		{
			startIt++;
			break;
		}
	}
}

void json::removeSpaces(std::string::iterator& startIt, std::string::iterator& endIt)
{
	while (startIt != endIt && ::isspace(*startIt))
		startIt++;
}

int json::countObjectOrArrayElements(std::string::iterator& startIt, std::string::iterator& endIt)
{
	int count = 0;
	for (; startIt != endIt; ++startIt)
	{
		skipObjectOrArray(startIt, endIt);
		count++;

		removeSpaces(startIt, endIt);

		if (startIt == endIt) break;

		if (*startIt != ',')
			break;
	}
	return count;
}

int json::countStringElements(std::string::iterator& startIt, std::string::iterator& endIt)
{
	int count = 0;

	for (; startIt != endIt; ++startIt)
	{
		skipString(startIt, endIt);
		count++;

		removeSpaces(startIt, endIt);

		if (startIt == endIt) break;

		if (*startIt != ',')
			break;
	}

	return count;
}